﻿#light

namespace Might.Modelica.Parsers

    open System.IO
    open Lexer       
    open Parser
    open AstTypes
    open AstOperations
    open AstDomAdapter
    open Microsoft.Practices.Unity
    open Microsoft.Practices.Composite.Events
    open Might.Infrastructure.Events
    open Microsoft.Practices.Composite.Presentation.Events
    open Might.Infrastructure.DOM
    open Might.Infrastructure.Services
    open System.Collections.Generic
    open Might.Infrastructure.ModelicaDOM
    open DomTypesImpl
    
    type ParsingExceptionEx(   currentTok:string, reduceTok: string, shiftTok: string, statStck: string, msg:string ) =        
        inherit System.Exception() 
        member x.currenToken = currentTok
        member x.reduceTokens = reduceTok
        member x.shiftTokens = shiftTok
        member x.stateStack = statStck
        override x.Message = msg
    
    
    /// <summary>
    /// Class encapsulating annotation parsing </summary>
    
    //IUnityContainer container, IEventAggregator eventAggregator
                
    type public ModelicaParser() = class
                                                    
        static member concatList(tokens) =
            let accumulate acc x =
                acc + System.Environment.NewLine + x            
            List.fold accumulate "" tokens
          
        static member getTokenName(tknID) =
            match tknID with              
                | Parser.tokenId.TOKEN_EOF -> "EOF" 
                | Parser.tokenId.TOKEN_TRUE -> "TRUE" 
                | Parser.tokenId.TOKEN_FALSE -> "FALSE" 
                | Parser.tokenId.TOKEN_OP_LBRACE -> "OP_LBRACE" 
                | Parser.tokenId.TOKEN_OP_RBRACE -> "OP_RBRACE" 
                | Parser.tokenId.TOKEN_OP_LBRACK -> "OP_LBRACK" 
                | Parser.tokenId.TOKEN_OP_RBRACK -> "OP_RBRACK" 
                | Parser.tokenId.TOKEN_OP_COMMA -> "OP_COMMA" 
                | Parser.tokenId.TOKEN_OP_ASSIGN -> "OP_ASSIGN" 
                | Parser.tokenId.TOKEN_OP_NAMED_ASSIGN -> "OP_NAMED_ASSIGN" 
                | Parser.tokenId.TOKEN_OP_SEMICOLON -> "OP_SEMICOLON" 
                | Parser.tokenId.TOKEN_OP_COLON -> "OP_COLON" 
                | Parser.tokenId.TOKEN_KW_AND -> "KW_AND" 
                | Parser.tokenId.TOKEN_KW_OR -> "KW_OR" 
                | Parser.tokenId.TOKEN_KW_NOT -> "KW_NOT" 
                | Parser.tokenId.TOKEN_OP_AR_MULTIPLICATION_ARRAY -> "OP_AR_MULTIPLICATION_ARRAY" 
                | Parser.tokenId.TOKEN_OP_AR_DIVISION_ARRAY -> "OP_AR_DIVISION_ARRAY" 
                | Parser.tokenId.TOKEN_OP_AR_POWER -> "OP_AR_POWER" 
                | Parser.tokenId.TOKEN_OP_AR_POWER_ARRAY -> "OP_AR_POWER_ARRAY" 
                | Parser.tokenId.TOKEN_OP_DOT -> "OP_DOT" 
                | Parser.tokenId.TOKEN_OP_LPAREN -> "OP_LPAREN" 
                | Parser.tokenId.TOKEN_OP_RPAREN -> "OP_RPAREN" 
                | Parser.tokenId.TOKEN_OP_REL_LESS -> "OP_REL_LESS" 
                | Parser.tokenId.TOKEN_OP_REL_GREAT -> "OP_REL_GREAT" 
                | Parser.tokenId.TOKEN_OP_REL_NEQUAL -> "OP_REL_NEQUAL" 
                | Parser.tokenId.TOKEN_OP_AR_ADDITION_ADD -> "OP_AR_ADDITION_ADD" 
                | Parser.tokenId.TOKEN_OP_AR_ADDITION_SUBSTRACT -> "OP_AR_ADDITION_SUBSTRACT" 
                | Parser.tokenId.TOKEN_OP_AR_ADDITION_ARRAY_ADD -> "OP_AR_ADDITION_ARRAY_ADD" 
                | Parser.tokenId.TOKEN_OP_AR_ADDITION_ARRAY_SUBSTRACT -> "OP_AR_ADDITION_ARRAY_SUBSTRACT" 
                | Parser.tokenId.TOKEN_OP_AR_MULTIPLICATION -> "OP_AR_MULTIPLICATION" 
                | Parser.tokenId.TOKEN_OP_AR_DIVISION -> "OP_AR_DIVISION" 
                | Parser.tokenId.TOKEN_KW_DER -> "KW_DER" 
                | Parser.tokenId.TOKEN_OP_REL_EQLESS -> "OP_REL_EQLESS" 
                | Parser.tokenId.TOKEN_OP_REL_EQGREAT -> "OP_REL_EQGREAT" 
                | Parser.tokenId.TOKEN_OP_REL_EQUAL -> "OP_REL_EQUAL" 
                | Parser.tokenId.TOKEN_KW_PUBLIC -> "KW_PUBLIC" 
                | Parser.tokenId.TOKEN_KW_IMPORT -> "KW_IMPORT" 
                | Parser.tokenId.TOKEN_KW_RECORD -> "KW_RECORD" 
                | Parser.tokenId.TOKEN_KW_REDECLARE -> "KW_REDECLARE" 
                | Parser.tokenId.TOKEN_KW_REPLACEABLE -> "KW_REPLACEABLE" 
                | Parser.tokenId.TOKEN_KW_RETURN -> "KW_RETURN" 
                | Parser.tokenId.TOKEN_KW_THEN -> "KW_THEN" 
                | Parser.tokenId.TOKEN_KW_TYPE -> "KW_TYPE" 
                | Parser.tokenId.TOKEN_KW_WHEN -> "KW_WHEN" 
                | Parser.tokenId.TOKEN_KW_WHILE -> "KW_WHILE" 
                | Parser.tokenId.TOKEN_KW_WITHIN -> "KW_WITHIN" 
                | Parser.tokenId.TOKEN_KW_INNER -> "KW_INNER" 
                | Parser.tokenId.TOKEN_KW_INPUT -> "KW_INPUT" 
                | Parser.tokenId.TOKEN_KW_LOOP -> "KW_LOOP" 
                | Parser.tokenId.TOKEN_KW_MODEL -> "KW_MODEL" 
                | Parser.tokenId.TOKEN_KW_OUTER -> "KW_OUTER" 
                | Parser.tokenId.TOKEN_KW_OUTPUT -> "KW_OUTPUT" 
                | Parser.tokenId.TOKEN_KW_PACKAGE -> "KW_PACKAGE" 
                | Parser.tokenId.TOKEN_KW_PARAMETER -> "KW_PARAMETER" 
                | Parser.tokenId.TOKEN_KW_PARTIAL -> "KW_PARTIAL" 
                | Parser.tokenId.TOKEN_KW_PROTECTED -> "KW_PROTECTED" 
                | Parser.tokenId.TOKEN_KW_EQUATION -> "KW_EQUATION" 
                | Parser.tokenId.TOKEN_KW_EXPANDABLE -> "KW_EXPANDABLE" 
                | Parser.tokenId.TOKEN_KW_EXTENDS -> "KW_EXTENDS" 
                | Parser.tokenId.TOKEN_KW_EXTERNAL -> "KW_EXTERNAL" 
                | Parser.tokenId.TOKEN_KW_FINAL -> "KW_FINAL" 
                | Parser.tokenId.TOKEN_KW_FLOW -> "KW_FLOW" 
                | Parser.tokenId.TOKEN_KW_FOR -> "KW_FOR" 
                | Parser.tokenId.TOKEN_KW_FUNCTION -> "KW_FUNCTION" 
                | Parser.tokenId.TOKEN_KW_IF -> "KW_IF" 
                | Parser.tokenId.TOKEN_KW_IN -> "KW_IN" 
                | Parser.tokenId.TOKEN_KW_INITIAL -> "KW_INITIAL" 
                | Parser.tokenId.TOKEN_KW_CONSTRAINEDBY -> "KW_CONSTRAINEDBY" 
                | Parser.tokenId.TOKEN_KW_DISCRETE -> "KW_DISCRETE" 
                | Parser.tokenId.TOKEN_KW_EACH -> "KW_EACH" 
                | Parser.tokenId.TOKEN_KW_ELSE -> "KW_ELSE" 
                | Parser.tokenId.TOKEN_KW_ELSEIF -> "KW_ELSEIF" 
                | Parser.tokenId.TOKEN_KW_ELSEWHEN -> "KW_ELSEWHEN" 
                | Parser.tokenId.TOKEN_KW_ENCAPSULATED -> "KW_ENCAPSULATED" 
                | Parser.tokenId.TOKEN_KW_END -> "KW_END" 
                | Parser.tokenId.TOKEN_KW_ENUMERATION -> "KW_ENUMERATION" 
                | Parser.tokenId.TOKEN_KW_ALGORITHM -> "KW_ALGORITHM" 
                | Parser.tokenId.TOKEN_KW_ANNOTATION -> "KW_ANNOTATION" 
                | Parser.tokenId.TOKEN_KW_ASSERT -> "KW_ASSERT" 
                | Parser.tokenId.TOKEN_KW_BLOCK -> "KW_BLOCK" 
                | Parser.tokenId.TOKEN_KW_BREAK -> "KW_BREAK" 
                | Parser.tokenId.TOKEN_KW_CLASS -> "KW_CLASS" 
                | Parser.tokenId.TOKEN_KW_CONNECT -> "KW_CONNECT" 
                | Parser.tokenId.TOKEN_KW_CONNECTOR -> "KW_CONNECTOR" 
                | Parser.tokenId.TOKEN_KW_CONSTANT -> "KW_CONSTANT" 
                | Parser.tokenId.TOKEN_IDENT -> "IDENT" 
                | Parser.tokenId.TOKEN_STRING -> "STRING"                
                | Parser.tokenId.TOKEN_FLOAT -> "FLOAT" 
                | Parser.tokenId.TOKEN_INT -> "INT" 
                | Parser.tokenId.TOKEN_end_of_input -> "TOKEN_end_of_input" 
                | Parser.tokenId.TOKEN_error -> "TOKEN_error" 
            
        
        static member intTokensToString tokenList = 
            let accumulate acc x =
                let tknID = Parser.tokenTagToTokenId x
                acc + " " + ModelicaParser.getTokenName tknID                
            List.fold accumulate "" tokenList  

        static member intListToString intList = 
            let accumulate acc x =
                acc + " " + x.ToString()
            List.fold accumulate "" intList  
        
        /// <summary> 
        ///Parses the annotation given in argument string </summary>
        /// <param name="annotationString">
        /// String with annotation to parse </param>
        /// <returns>
        /// Icon object build from parsed annotation </returns>
        static member Parse( source :byte array,fileName:string ) =
            // create buffer for lexing
            //let lexbuf = Microsoft.FSharp.Text.Lexing. from_bytearray source
            let lexbuf = Microsoft.FSharp.Text.Lexing.LexBuffer<_>.FromBytes(source)
            lexbuf.EndPos <- { pos_bol = 0;
                         pos_fname=fileName; 
                         pos_cnum=0;
                         pos_lnum=1 }

            // Call the parser 
            try                
                Parser.start Lexer.token lexbuf                
            with
                | :? ParsingException<'tok> as e ->                           
                    let pos = lexbuf.EndPos
                    System.Console.WriteLine("Parsing error  exception: " + e.Message)
                  
                    //let currentToken = Parser.token_to_string (e.currenToken :?> Parser.token)
                    let currentToken =
                        match e.currenToken with
                            | Some tk -> Parser.token_to_string tk
                            | None -> "Unknown token" 
                    let reduceTokens = ModelicaParser.intTokensToString e.reduceTokens
                    let shiftTokens = ModelicaParser.intTokensToString e.shiftTokens
                    let stateStack = ModelicaParser.intListToString e.stateStack
                    let message = "Parsing error at line " + (pos.Line + 1).ToString() + " column " + (pos.Column+1).ToString()
                    raise ( ParsingExceptionEx(currentToken,reduceTokens,shiftTokens,stateStack,message) )
                | _ as e -> 
                    raise(e)
                    
        static member public fileDownloadedEventHandler(file:IFile) =
            System.Console.WriteLine("I should be parsing and filling dom right now.....")
            
                
    end
    
    type ModelicaLanguageService(container:IUnityContainer, eventAggregator:IEventAggregator) = class
        let _container = container
        let _eventAggregator = eventAggregator
        
        static let mutable firstRun: bool = true
        static let mutable AstRoot = new Class()        
        
        static let mutable files = new Dictionary<string,string>()
        
        member this.GetSourceForModelicaClass(cls:IModelicaClass) =
            let sourceStartPosition = (cls :?> ModelicaClass)._startPosition
            let sourceEndPosition = (cls :?> ModelicaClass)._endPosition
            
            let sourceCode = files.[sourceStartPosition.FileName]
            
            sourceCode.Substring(sourceStartPosition.AbsoluteOffset,sourceEndPosition.AbsoluteOffset - sourceStartPosition.AbsoluteOffset)
        
        member this.ProcessFile(file:IFile) =
            
            let domRoot = _container.Resolve(typeof<IDom>) :?> IDom
                                                
            let parseStart = System.DateTime.Now
            
            if firstRun = true then
               AstDomAdapter.container <- _container
               firstRun <- false
               prepareDomRoot(domRoot)               
            
            //let progresToken = ((_container.Resolve(typeof<IProgressService>)) :?> IProgressService).ProgressCreate(typeof<FileDownloadedEvent>,file)            
            //progresToken.Progress(50,"Parsing")            
            
            let byteArray = file.Data.ToArray()
            
            let enc = new System.Text.UTF8Encoding()
            files.[file.Name] <- enc.GetString(byteArray,0,byteArray.Length)
            
            let rawAst = ModelicaParser.Parse(byteArray,file.Name)            
            resolveClassAndInsertIntoDom(rawAst,domRoot)                        
            //progresToken.ProgressEnd(ProgressResult.Success,"Parsed")
            let parseLength = System.DateTime.Now - parseStart
            let logSvc = _container.Resolve(typeof<IMightLogger>) :?> IMightLogger
            logSvc.Log(this,"Parsing and DOM processing of file " + file.Name + " took " + parseLength.TotalSeconds.ToString() + " seconds." )
            ()        
    end
    
    /// FOR TESTING PURPOSE ONLY
 (*   
    type TEST_ModelicaLanguageService() = class        
        
        static let mutable firstRun: bool = true
        static let mutable AstRoot = new Class()        
                        
        member this.processFile(file:byte array,domRoot:IDom,fileName) =
            
            //let domRoot = _container.Resolve(typeof<IDom>) :?> IDom
            
            let parseStart = System.DateTime.Now
            
            //if firstRun = true then
               //AstDomAdapter.testDomRoot <- domRoot
            AstDomAdapter.logMessages <- ""
            firstRun <- false
            prepareDomRoot(domRoot)               
            
            //let progresToken = ((_container.Resolve(typeof<IProgressService>)) :?> IProgressService).ProgressCreate(typeof<FileDownloadedEvent>,file)            
            //progresToken.Progress(50,"Parsing")            
            
            let byteArray = file
            let rawAst = ModelicaParser.Parse(byteArray,fileName)            
            resolveClassAndInsertIntoDom(rawAst,domRoot)                        
            //progresToken.ProgressEnd(ProgressResult.Success,"Parsed")
            let parseLength = System.DateTime.Now - parseStart
            //let logSvc = _container.Resolve(typeof<IMightLogger>) :?> IMightLogger                        
            AstDomAdapter.logMessages
    end
   *) 
(*   
    type Test() = class
        member public this.fileDownloadedEventHandler(file:IFile) = ()
    end
    
    
    type public ModelicaDOMService(container:IUnityContainer, eventAggregator:IEventAggregator) = class
        let mutable _container = container
        let mutable _eventAggregator = eventAggregator     
        
        do
            let fileEvent = eventAggregator.GetEvent<FileDownloadedEvent>() 
            
            let x = new Test()
                     
            //let subsToken = fileEvent.Subscribe( (fun(i)-> ModelicaParser.fileDownloadedEventHandler(i)), ThreadOption.UIThread, false);  
            //let sysAction = new System.Action((fun(i:IFile)-> x.fileDownloadedEventHandler(i)))
            //let fnc = fun(i:IFile)-> x.fileDownloadedEventHandler(i)
            let subsToken = fileEvent.Subscribe( (fun(i:IFile)-> x.fileDownloadedEventHandler(i)), ThreadOption.UIThread, false);            
            ()    
    end
    *)
        (*         
        static member parseAndReturnTokenString( source:string) =            
            let tokens = ModelicaParser.Parse source
            ModelicaParser.concatList tokens
            *)